home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 10 - 1994 / 10.10 Oct 94 / Sprocket / Lib / DialogUtils.cp < prev    next >
Encoding:
Text File  |  1994-08-25  |  9.3 KB  |  364 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        DialogUtils.cp
  3.  
  4.     Contains:    Auto-sized error alert mechanism,
  5.                 ModalFilterProcs which correctly handle events,
  6.                 and Standard “Close Document” dialogs. 
  7.  
  8.     Written by: Dave Falkenburg
  9.  
  10.     Copyright:    © 1993-94 by Dave Falkenburg, all rights reserved.
  11.  
  12.     Change History (most recent first):
  13.      
  14.  */
  15.  
  16. #include <Types.h>
  17. #include <Memory.h>
  18. #include <Quickdraw.h>
  19. #include <Fonts.h>
  20. #include <Resources.h>
  21. #include <Windows.h>
  22. #include <Dialogs.h>
  23. #include <TextUtils.h>
  24. #include <Threads.h>        //    For YieldToAnyThread()
  25. #include <StandardFile.h>    //    For ModalFilterYDProcPtr
  26. #include <SegLoad.h>
  27.  
  28. #include "AppLib.h"
  29.  
  30.  
  31. //    Some types which should probably be defined in <Dialogs.h>
  32. //    NOTE: These must be aligned on 2-byte boundaries
  33. #if defined(powerc) || defined (__powerc)
  34. #pragma options align=mac68k
  35. #endif
  36.  
  37. struct DialogItem
  38.     {
  39.     long    usedByDialogManager;
  40.     Rect    boundsRect;
  41.     char    type;
  42.     char    length;
  43.     };
  44.  
  45. struct DialogItemList            //    a.k.a. a 'DITL'
  46.     {
  47.     short        count;
  48.     DialogItem    firstItem[1];
  49.     };
  50.  
  51. //    Restore default alignment
  52. #if defined(powerc) || defined(__powerc)
  53. #pragma options align=reset
  54. #endif
  55.  
  56. typedef    DialogItem        *DialogItemPtr;
  57. typedef    DialogItemList    **DialogItemListHandle;
  58. typedef    DialogTemplate    **DialogTemplateHandle;
  59.  
  60.  
  61. //    private function Prototypes
  62.  
  63. static    pascal Boolean    StandardDialogFilterProc(DialogPtr theDialog, EventRecord * anEvent, short * itemHit);
  64. static    pascal Boolean    StandardDialogFilterYDProc(DialogPtr theDialog, EventRecord * anEvent, short * itemHit, void * yourData);
  65. static    pascal Boolean    StandardCloseDialogFilterProc(DialogPtr theDialog, EventRecord * anEvent, short * itemHit);
  66. static    Boolean            FilterProcCommon(DialogPtr theDialog, EventRecord * anEvent, short * itemHit);
  67.  
  68.  
  69.  
  70. ///////////////////////////////////////////////////////////
  71. //
  72. //    StandardAlert
  73. //
  74. //    An alternative to Alert() which uses the extended
  75. //    Dialog Manager capabilities.
  76. //
  77. //    I’m not sure we really need this call, but it seems
  78. //    to do the trick just fine.
  79.  
  80. short
  81. StandardAlert(    short dlogID,
  82.                 short defaultItem,                /* = ok */
  83.                 short cancelItem,                /* = 0 */
  84.                 ModalFilterUPP customFilterProc    /* = nil */)
  85.     {
  86.     DialogPtr        theDialog;
  87.     short            itemHit = 0;
  88.     ModalFilterUPP    filterToUse;
  89.     
  90.     HiliteWindowsForModalDialog(false);
  91.  
  92.     theDialog = GetNewDialog(dlogID,nil,(WindowPtr) -1);
  93.     if (defaultItem)
  94.         SetDialogDefaultItem(theDialog,defaultItem);
  95.     if (cancelItem)
  96.         SetDialogCancelItem(theDialog,cancelItem);
  97.  
  98.     if (customFilterProc)
  99.         filterToUse = customFilterProc;
  100.     else
  101.         filterToUse = StandardDialogFilter;
  102.  
  103.     do
  104.         ModalDialog(filterToUse,&itemHit);
  105.     while (itemHit == 0);
  106.     
  107.     DisposeDialog(theDialog);
  108.  
  109.     HiliteWindowsForModalDialog(true);
  110.  
  111.     return itemHit;
  112.     }
  113.  
  114.  
  115. ///////////////////////////////////////////////////////////
  116. //
  117. //    ErrorAlert
  118. //
  119. //    A nice error reporting routine which presents an
  120. //    auto-sized alert box containing the supplied text.
  121. //
  122. //    NOTE:    This routine ASSUMES the following 'DITL'
  123. //            structure:
  124. //
  125. //            item #1 : an “OK” button
  126. //            item #2 : a static text item, somewhere above #1
  127. //
  128. //    NOTE:    We probably need to worry more about low
  129. //            memory conditions-- this can probably
  130. //            be handled by a custom GrowZoneProc and
  131. //            reserve memory area large enough to hold
  132. //            all the space we’d need.
  133. //
  134.  
  135. void
  136. ErrorAlert(short stringList,short whichString)
  137.     {
  138.     Str255                    errorString;
  139.     GrafPtr                    oldPort,windowMgrPort;
  140.     short                    oldFont;
  141.     DialogTemplateHandle    errorDialogTemplate;
  142.     DialogItemListHandle    errorDialogItems;
  143.     TEHandle                aTEHandle;
  144.     Rect                    textRect;
  145.     short                    textHeight;
  146.     short                    additionalSpaceNeeded;
  147.     DialogItemPtr            okButtonItem,errorTextItem;
  148.     const StringPtr            nullStr = (StringPtr) "\p";
  149.  
  150.     GetIndString(errorString,stringList,whichString);
  151.     
  152.     errorDialogTemplate = (DialogTemplateHandle) Get1Resource('DLOG',kErrorAlertID);
  153.     HLock((Handle) errorDialogTemplate);
  154.     
  155.     errorDialogItems = (DialogItemListHandle) Get1Resource('DITL',(**errorDialogTemplate).itemsID);
  156.     HLock((Handle) errorDialogItems);
  157.     
  158.     //    Find the dialog items
  159.     
  160.     okButtonItem = (**errorDialogItems).firstItem;
  161.     errorTextItem = (DialogItemPtr) ((Ptr) okButtonItem + sizeof(DialogItem) + okButtonItem->length);
  162.     
  163.     GetPort(&oldPort);
  164.     GetWMgrPort(&windowMgrPort);
  165.     SetPort(windowMgrPort);
  166.     oldFont = qd.thePort->txFont;
  167.     TextFont(systemFont);
  168.  
  169.     aTEHandle = TENew(&textRect,&textRect);
  170.     TESetText(&errorString[1],errorString[0],aTEHandle);
  171.     textHeight = (*aTEHandle)->lineHeight * (*aTEHandle)->nLines;
  172.     TEDispose(aTEHandle);
  173.  
  174.     additionalSpaceNeeded = textHeight - (errorTextItem->boundsRect.bottom
  175.                             - errorTextItem->boundsRect.top);
  176.  
  177.     if (additionalSpaceNeeded > 0)
  178.         {
  179.         (**errorDialogTemplate).boundsRect.bottom += additionalSpaceNeeded;
  180.         errorTextItem->boundsRect.bottom += additionalSpaceNeeded;
  181.         OffsetRect(&okButtonItem->boundsRect,0,additionalSpaceNeeded);
  182.         }
  183.         
  184.     TextFont(oldFont);
  185.     SetPort(oldPort);
  186.     
  187.     InitCursor();
  188.     ParamText(errorString,nullStr,nullStr,nullStr);
  189.  
  190.     (void) StandardAlert(kErrorAlertID);
  191.  
  192.     ReleaseResource((Handle) errorDialogTemplate);
  193.     ReleaseResource((Handle) errorDialogItems);
  194.     }
  195.  
  196.  
  197. ///////////////////////////////////////////////////////////
  198. //
  199. //    FatalErrorAlert
  200. //
  201. //    A companion to ErrorAlert which also kills the process.
  202. //
  203.  
  204. void
  205. FatalErrorAlert(short stringList,short whichString)
  206.     {
  207.     ErrorAlert(stringList,whichString);
  208.     ExitToShell();
  209.     }
  210.  
  211.  
  212. ///////////////////////////////////////////////////////////
  213. //
  214. //    StandardDialogFilter and StandardDialogFilterYD
  215. //
  216. //    These function takes care of routing events not meant
  217. //    for the dialog window to other parts of the application.
  218. //
  219. //    Use them as an alternative to passing a NIL ModalFilterProc
  220. //    to ModalDialog() and CustomGet(Put)File. Unlike the default
  221. //    filter, these routines properly processes update events
  222. //    to keep background processes running.
  223. //
  224. //    The Thread Manager, if present, is also called to yield
  225. //    control to other cooperative threads within the process.
  226. //
  227. //    Because of pascal calling conventions we need two separate
  228. //    routines, but this is minimized by sharing implementation
  229. //    in FilterProcCommon.
  230. //
  231.  
  232. ModalFilterUPP    StandardDialogFilter
  233. = NewModalFilterProc(StandardDialogFilterProc);
  234.  
  235. ModalFilterYDUPP    StandardDialogFilterYD
  236. = NewModalFilterYDProc(StandardDialogFilterYDProc);
  237.  
  238. ModalFilterUPP    StandardCloseDialogFilter
  239. = NewModalFilterProc(StandardCloseDialogFilterProc);
  240.  
  241.  
  242. pascal Boolean
  243. StandardDialogFilterProc(DialogPtr theDialog, EventRecord* anEvent, short* itemHit)
  244.     {
  245.     //    Call through common code to check for events we’d like to handle.
  246.     //    If that is unsuccessful, call through the System 7 StdFilterProc
  247.     //    to handle CR, “CMD-.” & ESC in an international-friendly manner.
  248.  
  249.     if (FilterProcCommon(theDialog, anEvent, itemHit))
  250.         return true;
  251.     else
  252.         return (StdFilterProc(theDialog, anEvent, itemHit));
  253.     }
  254.  
  255.  
  256. pascal Boolean
  257. StandardDialogFilterYDProc(DialogPtr theDialog, EventRecord* anEvent, short* itemHit, void * /*unusedData*/)
  258.     {
  259.     //    We don’t call through to StdFilterProc since the
  260.     //    Standard File Package already does everything we need.
  261.  
  262.     return FilterProcCommon(theDialog, anEvent, itemHit);
  263.     }
  264.  
  265. void
  266. PseudoClickInDialogItem(DialogPtr theDialog, short itemToClick)
  267.     {
  268.     Handle    itemHandle;
  269.     Rect    itemBox;
  270.     long    finalTicks;
  271.     short    itemType;
  272.     
  273.     GetDialogItem(theDialog,itemToClick,&itemType,&itemHandle,&itemBox);
  274.  
  275.     HiliteControl((ControlHandle) itemHandle,inButton);
  276.     Delay(8,&finalTicks);
  277.     HiliteControl((ControlHandle) itemHandle,0);
  278.     }
  279.  
  280.  
  281. pascal Boolean
  282. StandardCloseDialogFilterProc(DialogPtr theDialog, EventRecord* anEvent, short* itemHit)
  283.     {
  284.     if ((anEvent->what == keyDown))
  285.         {
  286.         char    c = anEvent->message & charCodeMask;
  287.         
  288.         if ((c == 'd') || (c == 'D'))                //    NOT INTERNATIONAL FRIENDLY!!!
  289.             {
  290.             *itemHit = kDontSaveDocument;
  291.             PseudoClickInDialogItem(theDialog,kDontSaveDocument);
  292.             return true;
  293.             }
  294.         }
  295.  
  296.     //    Return through the common code above so that default item processing can happen
  297.     return StandardDialogFilterProc(theDialog, anEvent, itemHit);
  298.     }
  299.  
  300.  
  301. Boolean
  302. FilterProcCommon(DialogPtr theDialog, EventRecord * anEvent, short * /* itemHit */)
  303.     {
  304.     switch (anEvent->what)
  305.         {
  306.         case updateEvt:
  307.         case activateEvt:
  308.             //     Update or activate for the dialog window?
  309.             if (theDialog == (DialogPtr) anEvent->message)
  310.                 break;
  311.  
  312.             //    no, fall through to HandleEvent            
  313.             
  314.         case diskEvt:
  315.             HandleEvent(anEvent);
  316.             return(false);
  317.  
  318.         default:
  319.             break;        
  320.         }
  321.  
  322.     if (gHasThreadManager)        //    If we have threads, let them run!
  323.         YieldToAnyThread();
  324.  
  325.     return false;                //    We didn’t handle the event
  326.     }
  327.  
  328.  
  329. //////////////////////////////////////////////////////////////////
  330. //
  331. //    StandardCloseDocument
  332. //
  333. //    Provides the standard human interface for closing a document
  334. //
  335. //    NOTE: When we make TDocument class, this will become a method
  336. //          and probably won’t need any parameters.
  337. //
  338. //    NOTE:    StandardCloseResult matches the dialog items for 
  339.  
  340. StandardCloseResult
  341. StandardCloseDocument(const StringPtr documentType, StringPtr documentName,
  342.                       Boolean hasNewEditions, Boolean quitting)
  343.     {
  344.     short        whichAlert;
  345.     short        whichString;
  346.     StringPtr    nullStr = (StringPtr) "\p";
  347.     Str255        reasonForClosingStr;
  348.  
  349.     if (hasNewEditions)
  350.         whichAlert = kStandardCloseWithNewPubsAlertID;
  351.     else
  352.         whichAlert = kStandardCloseAlertID;
  353.     
  354.     if (quitting)
  355.         whichString = kQuittingStr;
  356.     else
  357.         whichString = kClosingStr;
  358.  
  359.     GetIndString(reasonForClosingStr,kStandardCloseStrings,whichString);
  360.     ParamText(documentType,documentName,reasonForClosingStr,nullStr);
  361.     
  362.     return ((StandardCloseResult) StandardAlert(whichAlert,kSaveDocument,kCancelSaveDocument,StandardCloseDialogFilter));
  363.     }
  364.